﻿using System;
using System.Collections.Generic;
using System.Text;

using SugarWebApi.Commands;
using SugarWebApi.Processors;

namespace SugarWebApi
{

	public class SugarWebConext : IDisposable
	{

		#region Constrcutors

		protected internal SugarWebConext(
			string url)
		{
			this._url = url;
			this._soapV1 = new SoapV1Processor(this._url);
			this._soapV2 = new SoapV2Processor(this._url);
			this._restV2 = new RestV2Processor(this._url);
			this._sessionId = string.Empty;
		}

		#endregion

		#region Fields

		private const SugarWebApiType DefaultWebApiType = SugarWebApiType.SoapV1;

		private readonly RestV2Processor _restV2;
		private readonly SoapV1Processor _soapV1;
		private readonly SoapV2Processor _soapV2;
		
		private string _url;
		private string _sessionId;

		#endregion

		#region Properties

		public string Url
		{
			get { return this._url; }
			protected internal set
			{
				if (this.Url == value) return;
				this._url = value;
				this.OnUrlChanged(new EventArgs());
			}
		}

		public string SessionId
		{
			get { return this._sessionId; }
		}

		#endregion

		#region Methods

		protected virtual void OnUrlChanged(EventArgs e)
		{
			this._soapV1.Url = this.Url;
			this._soapV2.Url = this.Url;
			this._restV2.Url = this.Url;
			this._sessionId = string.Empty;
		}

		#region GetAvailableModules

		public ICollection<SugarModule> GetAvailableModules()
		{
			return GetAvailableModules(DefaultWebApiType);
		}

		public ICollection<SugarModule> GetAvailableModules(
			SugarWebApiType type)
		{
			return this.ExecuteCommand<GetAvailableModulesCommand, GetAvailableModulesResult>(
				type,
				new GetAvailableModulesParameters(this.SessionId)).Modules;
		}

		#endregion

		#region GetEntryList

		public ICollection<SugarEntry> GetEntryList(
			string moduleName,
			string query,
			string orderBy,
			int offset,
			string[] fields,
			int limit,
			bool delete)
		{
			return this.GetEntryList(moduleName, query, orderBy, offset, fields, limit, delete, DefaultWebApiType);
		}

		public ICollection<SugarEntry> GetEntryList(
			string moduleName,
			string query,
			string orderBy,
			int offset,
			string[] fields,
			int limit,
			bool delete,
			SugarWebApiType type)
		{
			return
				this.GetEntryListResult(moduleName, query, orderBy, offset, fields, limit, delete, type).Entries;
		}

		public GetEntryListResult GetEntryListResult(
			string moduleName,
			string query,
			string orderBy,
			int offset,
			string[] fields,
			int limit,
			bool delete,
			SugarWebApiType type)
		{
			return this.ExecuteCommand<GetEntryListCommand, GetEntryListResult>(
				type,
				new GetEntryListParameters(this.SessionId, moduleName)
				{
					Query = query,
					Order = orderBy,
					Offset = offset,
					Fields = fields,
					Limit = limit,
					Deleted = delete
				});
		}

		#endregion

		#region GetModuleFields

		public GetModuleFieldsResult GetModuleFields(
			string moduleName)
		{
			return this.GetModuleFields(moduleName, DefaultWebApiType);
		}

		public GetModuleFieldsResult GetModuleFields(
			string moduleName,
			SugarWebApiType type)
		{
			return this.ExecuteCommand<GetModuleFieldsCommand, GetModuleFieldsResult>(
				type,
				new GetModuleFieldsParameters(this.SessionId, moduleName));
		}

		#endregion

		#region Login

		public static SugarWebConext Login(
			string url,
			string username,
			string password)
		{
			return SugarWebConext.Login(url, username, password, string.Empty);
		}

		public static SugarWebConext Login(
			string url,
			string username,
			string password,
			string encryptionkey)
		{
			return SugarWebConext.Login(url, username, password, encryptionkey, DefaultWebApiType);
		}

		public static SugarWebConext Login(
			string url,
			string username,
			string password,
			string encryptionkey,
			SugarWebApiType apitype)
		{
			SugarWebConext instance = new SugarWebConext(url);
			instance.LoginInternal(username, password, encryptionkey, apitype);
			return instance;
		}

		protected internal void LoginInternal(
			string username,
			string password,
			string encryptionkey,
			SugarWebApiType apitype)
		{
			LoginResult result =
				this.ExecuteCommand<LoginCommand, LoginResult>(
					apitype,
					new LoginParameters(username, password, encryptionkey));
			
			if (result.Success) this._sessionId = result.SessionId;
			else throw new SugarWebException(string.Format("Can not login to Sugar instance. {0}", "a")); // result.RawResult.error.description));
		}

		#endregion

		#region Logout

		public void Logout()
		{
			this.Logout(DefaultWebApiType);
		}

		public void Logout(
			SugarWebApiType type)
		{
			this.ExecuteCommand<LogoutCommand, LogoutResult>(type, new LogoutParameters(this.SessionId));
			this._sessionId = string.Empty;
		}

		#endregion

		#region SetEntry

		public SetEntryResult SetEntry(
			string moduleName,
			IDictionary<string, object> values)
		{
			return this.SetEntry(moduleName, values, DefaultWebApiType);
		}

		public SetEntryResult SetEntry(
			string moduleName,
			IDictionary<string, object> values,
			SugarWebApiType type)
		{
			return this.ExecuteCommand<SetEntryCommand, SetEntryResult>(
				type,
				new SetEntryParameters(this.SessionId, moduleName, values));
		}

		#endregion

		#region IDisposable メンバ

		public void Dispose()
		{
			try
			{
				this.Logout();
			}
			catch (Exception ex)
			{
				Trace.Debug(ex.Message);
				Trace.Debug(ex.StackTrace);
			}
		}

		#endregion

		public TResut ExecuteCommand<TCommand, TResut>(
			SugarWebApiType apitype,
			SugarWebParameters parameter)
			where TCommand : SugarWebCommand
			where TResut : SugarWebResult
		{
			// create command object
			TCommand command = CreateCommand<TCommand>(apitype, parameter);
			// execute command
			return ExecuteCommand<TResut>(command);
		}

		public TResut ExecuteCommand<TResut>(
			SugarWebCommand command)
			where TResut : SugarWebResult
		{
			// execute command
			return command.Execute() as TResut;
		}

		public TCommand CreateCommand<TCommand>(
			SugarWebApiType apitype,
			SugarWebParameters parameter)
			where TCommand : SugarWebCommand
		{
			// get command processor by apitype
			SugarWebProcessor processor = GetCommandProcessor(apitype);

			// create concrete command
			if (typeof(TCommand).Equals(typeof(LoginCommand)))
				return new LoginCommand(this, processor, parameter as LoginParameters) as TCommand;
			else if (typeof(TCommand).Equals(typeof(GetAvailableModulesCommand)))
				return new GetAvailableModulesCommand(this, processor, parameter as GetAvailableModulesParameters) as TCommand;
			else if (typeof(TCommand).Equals(typeof(GetEntryListCommand)))
				return new GetEntryListCommand(this, processor, parameter as GetEntryListParameters) as TCommand;
			else if (typeof(TCommand).Equals(typeof(GetModuleFieldsCommand)))
				return new GetModuleFieldsCommand(this, processor, parameter as GetModuleFieldsParameters) as TCommand;
			else if (typeof(TCommand).Equals(typeof(SetEntryCommand)))
				return new SetEntryCommand(this, processor, parameter as SetEntryParameters) as TCommand;

			throw new NotImplementedException();
		}

		public SugarWebProcessor GetCommandProcessor(
			SugarWebApiType apitype)
		{
			switch (apitype)
			{
				case SugarWebApiType.SoapV1: return this._soapV1;
				case SugarWebApiType.SoapV2: return this._soapV2;
				case SugarWebApiType.RestV2: return this._restV2;
			}
			throw new NotImplementedException();
		}

		#endregion


		
	}

}
